Entity relationship diagrams

The “crow’s foot” that connects the tables together shows us how the columns in one table relate to the columns in another table. ## Types Of Statements A few statements include: * CREATE TABLE is a statement that creates a new table in a database. * DROP TABLE is a statement that removes a table in a database. * SELECT allows you to read data and display it. This is called a query.

SELECT & FROM Statements

Here you were introduced to two statements that will be used in every query you write * SELECT - what columns you want back. * FROM - what table you are querying from.

SELECT id, account_id, occurred_at
FROM orders;

LIMIT Statement

The LIMIT statement is useful when you want to see just the first few rows of a table. This can be much faster for loading than if we load the entire dataset. The LIMIT command is always the very last part of a query. An example of showing just the first 10 rows of the orders table with all of the columns might look like the following:

SELECT *
FROM orders
LIMIT 10

ORDER BY Statement

SELECT id, occurred_at, total_amt_usd
FROM orders
ORDER BY occurred_at
LIMIT 10;

We can ORDER BY more than one column at a time. The statement sorts according to columns listed from left first and those listed on the right after that.

SELECT *
FROM orders
ORDER BY occurred_at, total_amt_usd
LIMIT 10;

WHERE Statements

Using the WHERE statement, we can subset out tables based on conditions that must be met.

SELECT *
FROM orders
WHERE gloss_amt_usd >= 1000
LIMIT 5;

The WHERE statement can also be used with non-numerical data. We can use the = and != operators here. You also need to be sure to use single quotes

SELECT name, website, primary_poc
FROM accounts
WHERE name = 'Exxon Mobil';

Arithmetic Operators

Derived Columns

Creating a new column that is a combination of existing columns is known as a derived column. ### Order of Operations Remember BOMDAS from maths class? The same order of operations apply when using arithmetic operators in SQL.

SELECT id, account_id, 
       poster_amt_usd/(standard_amt_usd + gloss_amt_usd + poster_amt_usd) AS post_per
FROM orders;

Introduction to Logical Operators

LIKE - This allows you to perform operations similar to using WHERE and =, but for cases when you might not know exactly what you are looking for. The LIKE operator is frequently used with %. The % tells us that we might want any number of characters leading up to a particular set of characters or following a certain set of characters.

/* select all the companies whose names start with 'C' */
SELECT name 
FROM accounts
WHERE name like 'C%'

/* select All companies whose names contain the string 'one' somewhere in the name. */
SELECT name
FROM accounts
WHERE name LIKE '%one%';

IN - This allows you to perform operations similar to using WHERE and =, but for more than one condition. We can check one, two or many column values for which we want to pull data, but all within the same query. TIP - In most SQL environments, you can use single or double quotation marks - and you may NEED to use double quotation marks if you have an apostrophe within the text you are attempting to pull.

SELECT name, primary_poc, sales_rep_id
FROM accounts
WHERE name IN ('Walmart', 'Target', 'Nordstrom');

NOT - This is used with IN and LIKE to select all of the rows NOT LIKE or NOT IN a certain condition.

/* Use the accounts table to find the account name, primary poc, and sales rep id for all stores except Walmart, Target, and Nordstrom */
SELECT name, primary_poc, sales_rep_id
FROM accounts
WHERE name NOT IN ('Walmart', 'Target', 'Nordstrom');

AND & BETWEEN - These allow you to combine operations where all combined conditions must be true. * The AND operator is used within a WHERE statement to consider more than one logical clause at a time. Each time you link a new statement with an AND, you will need to specify the column you are interested in looking at. * Sometimes we can make a cleaner statement using BETWEEN than we can using AND. Particularly this is true when we are using the same column for different parts of our AND statement. e.g.:

/*Instead of writing : WHERE column >= 6 AND column <= 10 we can instead write*/ 
WHERE column BETWEEN 6 AND 10
/* Write a query that returns all the orders where the standard_qty is over 1000, the poster_qty is 0, and the gloss_qty is 0 */
SELECT *
FROM orders
WHERE standard_qty > 1000 AND poster_qty = 0 AND gloss_qty = 0;

/* Using the accounts table find all the companies whose names do not start with 'C' and end with 's' */
SELECT name
FROM accounts
WHERE name NOT LIKE 'C%' AND name LIKE '%s';

/* Use the web_events table to find all information regarding individuals who were contacted via organic or adwords and started their account at any point in 2016 sorted from newest to oldest */ 
SELECT *
FROM web_events
WHERE channel IN ('organic', 'adwords') AND occurred_at BETWEEN '2016-01-01' AND '2017-01-01'
ORDER BY occurred_at DESC;
/*  You will notice that using BETWEEN is tricky for dates! While BETWEEN is generally inclusive of endpoints, it assumes the time is at 00:00:00 (i.e. midnight) for dates. This is the reason why we set the right-side endpoint of the period at '2017-01-01'. */

OR - This allow you to combine operations where at least one of the combined conditions must be true. Similar to the AND operator, the OR operator can combine multiple statements. Each time you link a new statement with an OR, you will need to specify the column you are interested in looking at.

/* Find list of orders ids where either gloss_qty or poster_qty is greater than 4000. Only include the id field in the resulting table. */
SELECT id
FROM orders
WHERE gloss_qty > 4000 OR poster_qty > 4000;

/* Write a query that returns a list of orders where the standard_qty is zero and either the gloss_qty or poster_qty is over 1000. */
SELECT *
FROM orders
WHERE standard_qty = 0 AND (gloss_qty > 1000 OR poster_qty > 1000);

/* Find all the company names that start with a 'C' or 'W', and the primary contact contains 'ana' or 'Ana', but it doesn't contain 'eana'*/
SELECT *
FROM accounts
WHERE (name LIKE 'C%' OR name LIKE 'W%') 
           AND ((primary_poc LIKE '%ana%' OR primary_poc LIKE '%Ana%') 
           AND primary_poc NOT LIKE '%eana%');

Summary of basic comands:


JOINS

Inner joins

Only return rows that appear in both tables e.g. Using ERD: Pull standard_qty, gloss_qty, and poster_qty from the orders table, and the website and the primary_poc from the accounts table.

SELECT orders.standard_qty, orders.gloss_qty, 
       orders.poster_qty,  accounts.website, 
       accounts.primary_poc
FROM orders
JOIN accounts
ON orders.account_id = accounts.id

Notice that we need to specify every table a column comes from in the SELECT statement. ### ALIAS When we JOIN tables together, it is nice to give each table an alias. Frequently an alias is just the first letter of the table name.

FROM tablename t1 
JOIN tablename2 t2

Questions

  1. Provide a table for all web_events associated with account name of Walmart. There should be three columns. Be sure to include the primary_poc, time of the event, and the channel for each event. Additionally, you might choose to add a fourth column to assure only Walmart events were chosen.
SELECT a.primary_poc, w.occurred_at, w.channel, a.name
FROM web_events w
JOIN accounts a
ON w.account_id = a.id
WHERE a.name = 'Walmart';
  1. Provide a table that provides the region for each sales_rep along with their associated accounts. Your final table should include three columns: the region name, the sales rep name, and the account name. Sort the accounts alphabetically (A-Z) according to account name.
SELECT r.name region, s.name rep, a.name account
FROM sales_reps s
JOIN region r
ON s.region_id = r.id
JOIN accounts a
ON a.sales_rep_id = s.id
ORDER BY a.name;
  1. Provide the name for each region for every order, as well as the account name and the unit price they paid (total_amt_usd/total) for the order. Your final table should have 3 columns: region name, account name, and unit price. A few accounts have 0 for total, so I divided by (total + 0.01) to assure not dividing by zero.
SELECT r.name region, a.name account, 
       o.total_amt_usd/(o.total + 0.01) unit_price
FROM region r
JOIN sales_reps s
ON s.region_id = r.id
JOIN accounts a
ON a.sales_rep_id = s.id
JOIN orders o
ON o.account_id = a.id;

Outer joins

If we want to include data that only exists in one of the tables we ant to join we can use an outer join If there is not matching information in the JOINed table, then you will have columns with empty cells with a data type called NULL. Writing a LEFT join statement: Comparing LEFT and RIGHT join: LEFT and RIGHT joins are essentially inter changeable so its standard to just use LEFT joins.

JOINS and filtering

If you wanted to filter your results so that it only showed sales from a particular sales rep you could use WHERE but it’s better to use AND as part of the ON statement. A simple rule to remember this is that, when the database executes this query, it executes the join and everything in the ON clause first. Think of this as building the new result set. That result set is then filtered using the WHERE clause.

SELECT orders.*, 
accounts.*
FROM orders
LEFT JOIN accounts
ON orders.account_id = accounts.id
/* Use AND in stead of WHERE for filtering this LEFT JOIN */
AND accounts.sales_rep_id = 325 

Questions

  1. Provide a table that provides the region for each sales_rep along with their associated accounts. This time only for the Midwest region. Your final table should include three columns: the region name, the sales rep name, and the account name. Sort the accounts alphabetically (A-Z) according to account name.
SELECT r.name region, s.name rep, a.name account
FROM sales_reps s
JOIN region r
ON s.region_id = r.id
JOIN accounts a
ON a.sales_rep_id = s.id
WHERE r.name = 'Midwest'
ORDER BY a.name;
  1. Provide a table that provides the region for each sales_rep along with their associated accounts. This time only for accounts where the sales rep has a first name starting with S and in the Midwest region. Your final table should include three columns: the region name, the sales rep name, and the account name. Sort the accounts alphabetically (A-Z) according to account name.
SELECT r.name region, s.name rep, a.name account
FROM sales_reps s
JOIN region r
ON s.region_id = r.id
JOIN accounts a
ON a.sales_rep_id = s.id
WHERE r.name = 'Midwest' AND s.name LIKE 'S%'
ORDER BY a.name;
  1. Provide a table that provides the region for each sales_rep along with their associated accounts. This time only for accounts where the sales rep has a last name starting with K and in the Midwest region. Your final table should include three columns: the region name, the sales rep name, and the account name. Sort the accounts alphabetically (A-Z) according to account name.
SELECT r.name region, s.name rep, a.name account
FROM sales_reps s
JOIN region r
ON s.region_id = r.id
JOIN accounts a
ON a.sales_rep_id = s.id
WHERE r.name = 'Midwest' AND s.name LIKE '% K%'
ORDER BY a.name;
  1. Provide the name for each region for every order, as well as the account name and the unit price they paid (total_amt_usd/total) for the order. However, you should only provide the results if the standard order quantity exceeds 100. Your final table should have 3 columns: region name, account name, and unit price. In order to avoid a division by zero error, adding .01 to the denominator here is helpful total_amt_usd/(total+0.01).
SELECT r.name region, a.name account, o.total_amt_usd/(o.total + 0.01) unit_price
FROM region r
JOIN sales_reps s
ON s.region_id = r.id
JOIN accounts a
ON a.sales_rep_id = s.id
JOIN orders o
ON o.account_id = a.id
WHERE o.standard_qty > 100;
  1. Provide the name for each region for every order, as well as the account name and the unit price they paid (total_amt_usd/total) for the order. However, you should only provide the results if the standard order quantity exceeds 100 and the poster order quantity exceeds 50. Your final table should have 3 columns: region name, account name, and unit price. Sort for the smallest unit price first. In order to avoid a division by zero error, adding .01 to the denominator here is helpful (total_amt_usd/(total+0.01).
SELECT r.name region, a.name account, o.total_amt_usd/(o.total + 0.01) unit_price
FROM region r
JOIN sales_reps s
ON s.region_id = r.id
JOIN accounts a
ON a.sales_rep_id = s.id
JOIN orders o
ON o.account_id = a.id
WHERE o.standard_qty > 100 AND o.poster_qty > 50
ORDER BY unit_price;
  1. Provide the name for each region for every order, as well as the account name and the unit price they paid (total_amt_usd/total) for the order. However, you should only provide the results if the standard order quantity exceeds 100 and the poster order quantity exceeds 50. Your final table should have 3 columns: region name, account name, and unit price. Sort for the largest unit price first. In order to avoid a division by zero error, adding .01 to the denominator here is helpful (total_amt_usd/(total+0.01).
SELECT r.name region, a.name account, o.total_amt_usd/(o.total + 0.01) unit_price
FROM region r
JOIN sales_reps s
ON s.region_id = r.id
JOIN accounts a
ON a.sales_rep_id = s.id
JOIN orders o
ON o.account_id = a.id
WHERE o.standard_qty > 100 AND o.poster_qty > 50
ORDER BY unit_price DESC;
  1. What are the different channels used by account id 1001? Your final table should have only 2 columns: account name and the different channels. You can try SELECT DISTINCT to narrow down the results to only the unique values.
SELECT DISTINCT a.name, w.channel
FROM accounts a
RIGHT JOIN web_events w
ON a.id = w.account_id
WHERE a.id = '1001';
  1. Find all the orders that occurred in 2015. Your final table should have 4 columns: occurred_at, account name, order total, and order total_amt_usd.
SELECT o.occurred_at, a.name, o.total, o.total_amt_usd
FROM accounts a
JOIN orders o
ON o.account_id = a.id
WHERE o.occurred_at BETWEEN '01-01-2015' AND '01-01-2016'
ORDER BY o.occurred_at DESC;

AGGREGATIONS

Null values

When identifying NULLs in a WHERE clause, we write IS NULL or IS NOT NULL. We don’t use =, because NULL isn’t considered a value in SQL.

COUNT

Count the number of rows in a table

SELECT COUNT(*)
FROM accounts;

COUNT does not consider rows that have NULL values. Therefore, this can be useful for quickly identifying which rows have missing data.

SUM

Unlike COUNT, you can only use SUM on numeric columns. However, SUM will ignore NULL values and treat them as 0.

Questions

  1. Find the total amount of poster_qty paper ordered in the orders table.
SELECT SUM(poster_qty) AS total_poster_sales
FROM orders;
  1. Find the total amount of standard_qty paper ordered in the orders table.
SELECT SUM(standard_qty) AS total_standard_sales
FROM orders;
  1. Find the total dollar amount of sales using the total_amt_usd in the orders table.
SELECT SUM(total_amt_usd) AS total_dollar_sales
FROM orders;
  1. Find the total amount spent on standard_amt_usd and gloss_amt_usd paper for each order in the orders table. This should give a dollar amount for each order in the table. Notice, this solution did not use an aggregate.
SELECT standard_amt_usd + gloss_amt_usd AS total_standard_gloss
FROM orders;
  1. Find the standard_amt_usd per unit of standard_qty paper. Your solution should use both an aggregation and a mathematical operator. Notice, this solution used both an aggregate and our mathematical operators
SELECT SUM(standard_amt_usd)/SUM(standard_qty) AS standard_price_per_unit
FROM orders;

MIN and MAX

MIN and MAX are aggregators that ignore NULL values. Functionally, MIN and MAX are similar to COUNT in that they can be used on non-numerical columns. Depending on the column type, MIN will return the lowest number, earliest date, or non-numerical value as early in the alphabet as possible. As you might suspect, MAX does the opposite—it returns the highest number, the latest date, or the non-numerical value closest alphabetically to “Z.”

Questions

  1. When was the earliest order ever placed?
SELECT MIN(occurred_at) 
FROM orders;

Try performing the same query as in question 1 without using an aggregation function.

SELECT occurred_at 
FROM orders 
ORDER BY occurred_at
LIMIT 1;
  1. When did the most recent (latest) web_event occur?
SELECT MAX(occurred_at)
FROM web_events;

Try to perform the result of the previous query without using an aggregation function.

SELECT occurred_at
FROM web_events
ORDER BY occurred_at DESC
LIMIT 1;

AVG

AVG returns the mean of the data - that is the sum of all of the values in the column divided by the number of values in a column. This aggregate function ignores the NULL values in both the numerator and the denominator.

Questions

  1. Find the mean (AVERAGE) amount spent per order on each paper type, as well as the mean amount of each paper type purchased per order. Your final answer should have 6 values - one for each paper type for the average number of sales, as well as the average amount.
SELECT AVG(standard_qty) mean_standard, AVG(gloss_qty) mean_gloss, 
           AVG(poster_qty) mean_poster, AVG(standard_amt_usd) mean_standard_usd, 
           AVG(gloss_amt_usd) mean_gloss_usd, AVG(poster_amt_usd) mean_poster_usd
FROM orders;
  1. What is the MEDIAN total_usd spent on all orders? Note, this is more advanced than the topics we have covered thus far to build a general solution, but we can hard code a solution in the following way.
SELECT *
FROM (SELECT total_amt_usd
      FROM orders
      ORDER BY total_amt_usd
      LIMIT 3457) AS Table1
ORDER BY total_amt_usd DESC
LIMIT 2;

Since there are 6912 orders - we want the average of the 3457 and 3456 order amounts when ordered. This is the average of 2483.16 and 2482.55. This gives the median of 2482.855. This obviously isn’t an ideal way to compute. If we obtain new orders, we would have to change the limit. SQL didn’t even calculate the median for us. The above used a SUBQUERY, but you could use any method to find the two necessary values, and then you just need the average of them.

GROUP BY

GROUP BY can be used to aggregate data within subsets of the data. For example, grouping for different accounts, different regions, or different sales representatives. Any column in the SELECT statement that is not within an aggregator must be in the GROUP BY clause. The GROUP BY always goes between WHERE and ORDER BY. ORDER BY works like SORT in spreadsheet software. SQL evaluates the aggregations before the LIMIT clause. If you don’t group by any columns, you’ll get a 1-row result—no problem there. If you group by a column with enough unique values that it exceeds the LIMIT number, the aggregates will be calculated, and then some rows will simply be omitted from the results. This is a nice way to do things because you know you’re going to get the correct aggregates. If SQL cuts the table down to 100 rows, then performed the aggregations, your results would be substantially different.

Note: You can reference the columns in your select statement in GROUP BY and ORDER BY clauses with numbers that follow the order they appear in the select statement. For example

SELECT standard_qty, COUNT(*)

FROM orders

GROUP BY 1 /*(this 1 refers to standard_qty since it is the first of the columns included in the select statement)*/

ORDER BY 1 /*(this 1 refers to standard_qty since it is the first of the columns included in the select statement)*/

Questions

  1. Which account (by name) placed the earliest order? Your solution should have the account name and the date of the order.
SELECT a.name, o.occurred_at
FROM accounts a
JOIN orders o
ON a.id = o.account_id
ORDER BY occurred_at
LIMIT 1;
  1. Find the total sales in usd for each account. You should include two columns - the total sales for each company’s orders in usd and the company name.
SELECT a.name, SUM(total_amt_usd) total_sales
FROM orders o
JOIN accounts a
ON a.id = o.account_id
GROUP BY a.name;
  1. Via what channel did the most recent (latest) web_event occur, which account was associated with this web_event? Your query should return only three values - the date, channel, and account name.
SELECT w.occurred_at, w.channel, a.name
FROM web_events w
JOIN accounts a
ON w.account_id = a.id 
ORDER BY w.occurred_at DESC
LIMIT 1;
  1. Find the total number of times each type of channel from the web_events was used. Your final table should have two columns - the channel and the number of times the channel was used.
SELECT w.channel, COUNT(*)
FROM web_events w
GROUP BY w.channel
  1. Who was the primary contact associated with the earliest web_event?
SELECT a.primary_poc
FROM web_events w
JOIN accounts a
ON a.id = w.account_id
ORDER BY w.occurred_at
LIMIT 1;
  1. What was the smallest order placed by each account in terms of total usd. Provide only two columns - the account name and the total usd. Order from smallest dollar amounts to largest.
SELECT a.name, MIN(total_amt_usd) smallest_order
FROM accounts a
JOIN orders o
ON a.id = o.account_id
GROUP BY a.name
ORDER BY smallest_order;

Sort of strange we have a bunch of orders with no dollars. We might want to look into those.

  1. Find the number of sales reps in each region. Your final table should have two columns - the region and the number of sales_reps. Order from fewest reps to most reps.
SELECT r.name, COUNT(*) num_reps
FROM region r
JOIN sales_reps s
ON r.id = s.region_id
GROUP BY r.name
ORDER BY num_reps;

You can GROUP BY multiple columns at once. This is often useful to aggregate across a number of different segments. The order of columns listed in the ORDER BY clause does make a difference. You are ordering the columns from left to right. The order of column names in your GROUP BY clause doesn’t matter—the results will be the same regardless.

Questions

  1. For each account, determine the average amount of each type of paper they purchased across their orders. Your result should have four columns - one for the account name and one for the average spent on each of the paper types.
SELECT a.name, AVG(o.standard_qty) avg_stand, AVG(o.gloss_qty) avg_gloss, AVG(o.poster_qty) avg_post
FROM accounts a
JOIN orders o
ON a.id = o.account_id
GROUP BY a.name;
  1. For each account, determine the average amount spent per order on each paper type. Your result should have four columns - one for the account name and one for the average amount spent on each paper type.
SELECT a.name, AVG(o.standard_amt_usd) avg_stand, AVG(o.gloss_amt_usd) avg_gloss, AVG(o.poster_amt_usd) avg_post
FROM accounts a
JOIN orders o
ON a.id = o.account_id
GROUP BY a.name;
  1. Determine the number of times a particular channel was used in the web_events table for each sales rep. Your final table should have three columns - the name of the sales rep, the channel, and the number of occurrences. Order your table with the highest number of occurrences first.
SELECT s.name, w.channel, COUNT(*) num_events
FROM accounts a
JOIN web_events w
ON a.id = w.account_id
JOIN sales_reps s
ON s.id = a.sales_rep_id
GROUP BY s.name, w.channel
ORDER BY num_events DESC;
  1. Determine the number of times a particular channel was used in the web_events table for each region. Your final table should have three columns - the region name, the channel, and the number of occurrences. Order your table with the highest number of occurrences first.
SELECT r.name, w.channel, COUNT(*) num_events
FROM accounts a
JOIN web_events w
ON a.id = w.account_id
JOIN sales_reps s
ON s.id = a.sales_rep_id
JOIN region r
ON r.id = s.region_id
GROUP BY r.name, w.channel
ORDER BY num_events DESC;
LS0tCnRpdGxlOiAiTm90ZXMgb24gU1FMIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiMjIEVudGl0eSByZWxhdGlvbnNoaXAgZGlhZ3JhbXMKVGhlICJjcm93J3MgZm9vdCIgdGhhdCBjb25uZWN0cyB0aGUgdGFibGVzIHRvZ2V0aGVyIHNob3dzIHVzIGhvdyB0aGUgY29sdW1ucyBpbiBvbmUgdGFibGUgcmVsYXRlIHRvIHRoZSBjb2x1bW5zIGluIGFub3RoZXIgdGFibGUuIAohW10oaW1hZ2VzL0VSRC5wbmcpCiMjIFR5cGVzIE9mIFN0YXRlbWVudHMKIEEgZmV3IHN0YXRlbWVudHMgaW5jbHVkZToKKiBDUkVBVEUgVEFCTEUgaXMgYSBzdGF0ZW1lbnQgdGhhdCBjcmVhdGVzIGEgbmV3IHRhYmxlIGluIGEgZGF0YWJhc2UuCiogRFJPUCBUQUJMRSBpcyBhIHN0YXRlbWVudCB0aGF0IHJlbW92ZXMgYSB0YWJsZSBpbiBhIGRhdGFiYXNlLgoqIFNFTEVDVCBhbGxvd3MgeW91IHRvIHJlYWQgZGF0YSBhbmQgZGlzcGxheSBpdC4gVGhpcyBpcyBjYWxsZWQgYSBxdWVyeS4KCiMjIFNFTEVDVCAmIEZST00gU3RhdGVtZW50cwpIZXJlIHlvdSB3ZXJlIGludHJvZHVjZWQgdG8gdHdvIHN0YXRlbWVudHMgdGhhdCB3aWxsIGJlIHVzZWQgaW4gZXZlcnkgcXVlcnkgeW91IHdyaXRlCiogU0VMRUNUIC0gd2hhdCBjb2x1bW5zIHlvdSB3YW50IGJhY2suCiogRlJPTSAtIHdoYXQgdGFibGUgeW91IGFyZSBxdWVyeWluZyBmcm9tLiAKYGBge3NxbH0KU0VMRUNUIGlkLCBhY2NvdW50X2lkLCBvY2N1cnJlZF9hdApGUk9NIG9yZGVyczsKYGBgCgojIyBMSU1JVCBTdGF0ZW1lbnQKIFRoZSBMSU1JVCBzdGF0ZW1lbnQgaXMgdXNlZnVsIHdoZW4geW91IHdhbnQgdG8gc2VlIGp1c3QgdGhlIGZpcnN0IGZldyByb3dzIG9mIGEgdGFibGUuIFRoaXMgY2FuIGJlIG11Y2ggZmFzdGVyIGZvciBsb2FkaW5nIHRoYW4gaWYgd2UgbG9hZCB0aGUgZW50aXJlIGRhdGFzZXQuClRoZSBMSU1JVCBjb21tYW5kIGlzIGFsd2F5cyB0aGUgdmVyeSBsYXN0IHBhcnQgb2YgYSBxdWVyeS4gQW4gZXhhbXBsZSBvZiBzaG93aW5nIGp1c3QgdGhlIGZpcnN0IDEwIHJvd3Mgb2YgdGhlIG9yZGVycyB0YWJsZSB3aXRoIGFsbCBvZiB0aGUgY29sdW1ucyBtaWdodCBsb29rIGxpa2UgdGhlIGZvbGxvd2luZzoKYGBge3NxbH0KU0VMRUNUICoKRlJPTSBvcmRlcnMKTElNSVQgMTAKYGBgCgojIyBPUkRFUiBCWSBTdGF0ZW1lbnQKKiBUaGUgT1JERVIgQlkgc3RhdGVtZW50IGFsbG93cyB1cyB0byBvcmRlciBvdXIgdGFibGUgYnkgYW55IHJvdy4gCiogUmVtZW1iZXIgREVTQyBjYW4gYmUgYWRkZWQgYWZ0ZXIgdGhlIGNvbHVtbiBpbiB5b3VyIE9SREVSIEJZIHN0YXRlbWVudCB0byBzb3J0IGluIGRlc2NlbmRpbmcgb3JkZXIsIGFzIHRoZSBkZWZhdWx0IGlzIHRvIHNvcnQgaW4gYXNjZW5kaW5nIG9yZGVyLgpgYGB7c3FsfQpTRUxFQ1QgaWQsIG9jY3VycmVkX2F0LCB0b3RhbF9hbXRfdXNkCkZST00gb3JkZXJzCk9SREVSIEJZIG9jY3VycmVkX2F0CkxJTUlUIDEwOwpgYGAKCldlIGNhbiBPUkRFUiBCWSBtb3JlIHRoYW4gb25lIGNvbHVtbiBhdCBhIHRpbWUuIFRoZSBzdGF0ZW1lbnQgc29ydHMgYWNjb3JkaW5nIHRvIGNvbHVtbnMgbGlzdGVkIGZyb20gbGVmdCBmaXJzdCBhbmQgdGhvc2UgbGlzdGVkIG9uIHRoZSByaWdodCBhZnRlciB0aGF0LgpgYGB7c3FsfQpTRUxFQ1QgKgpGUk9NIG9yZGVycwpPUkRFUiBCWSBvY2N1cnJlZF9hdCwgdG90YWxfYW10X3VzZApMSU1JVCAxMDsKYGBgCgojIyBXSEVSRSBTdGF0ZW1lbnRzClVzaW5nIHRoZSAqKldIRVJFKiogc3RhdGVtZW50LCB3ZSBjYW4gc3Vic2V0IG91dCB0YWJsZXMgYmFzZWQgb24gY29uZGl0aW9ucyB0aGF0IG11c3QgYmUgbWV0LgpgYGB7c3FsfQpTRUxFQ1QgKgpGUk9NIG9yZGVycwpXSEVSRSBnbG9zc19hbXRfdXNkID49IDEwMDAKTElNSVQgNTsKYGBgCgpUaGUgV0hFUkUgc3RhdGVtZW50IGNhbiBhbHNvIGJlIHVzZWQgd2l0aCBub24tbnVtZXJpY2FsIGRhdGEuIFdlIGNhbiB1c2UgdGhlID0gYW5kICE9IG9wZXJhdG9ycyBoZXJlLiBZb3UgYWxzbyBuZWVkIHRvIGJlIHN1cmUgdG8gdXNlIHNpbmdsZSBxdW90ZXMKYGBge3NxbH0KU0VMRUNUIG5hbWUsIHdlYnNpdGUsIHByaW1hcnlfcG9jCkZST00gYWNjb3VudHMKV0hFUkUgbmFtZSA9ICdFeHhvbiBNb2JpbCc7CmBgYAoKIyMgQXJpdGhtZXRpYyBPcGVyYXRvcnMKIyMjIERlcml2ZWQgQ29sdW1ucwpDcmVhdGluZyBhIG5ldyBjb2x1bW4gdGhhdCBpcyBhIGNvbWJpbmF0aW9uIG9mIGV4aXN0aW5nIGNvbHVtbnMgaXMga25vd24gYXMgYSBkZXJpdmVkIGNvbHVtbi4KIyMjIE9yZGVyIG9mIE9wZXJhdGlvbnMKUmVtZW1iZXIgQk9NREFTIGZyb20gbWF0aHMgY2xhc3M/IFRoZSBzYW1lIG9yZGVyIG9mIG9wZXJhdGlvbnMgYXBwbHkgd2hlbiB1c2luZyBhcml0aG1ldGljIG9wZXJhdG9ycyBpbiBTUUwuCmBgYHtzcWx9ClNFTEVDVCBpZCwgYWNjb3VudF9pZCwgCiAgICAgICBwb3N0ZXJfYW10X3VzZC8oc3RhbmRhcmRfYW10X3VzZCArIGdsb3NzX2FtdF91c2QgKyBwb3N0ZXJfYW10X3VzZCkgQVMgcG9zdF9wZXIKRlJPTSBvcmRlcnM7CmBgYAojIyMgSW50cm9kdWN0aW9uIHRvIExvZ2ljYWwgT3BlcmF0b3JzCgoqKkxJS0UqKiAtIFRoaXMgYWxsb3dzIHlvdSB0byBwZXJmb3JtIG9wZXJhdGlvbnMgc2ltaWxhciB0byB1c2luZyBXSEVSRSBhbmQgPSwgYnV0IGZvciBjYXNlcyB3aGVuIHlvdSBtaWdodCBub3Qga25vdyBleGFjdGx5IHdoYXQgeW91IGFyZSBsb29raW5nIGZvci4gIFRoZSBMSUtFIG9wZXJhdG9yIGlzIGZyZXF1ZW50bHkgdXNlZCB3aXRoICUuIFRoZSAlIHRlbGxzIHVzIHRoYXQgd2UgbWlnaHQgd2FudCBhbnkgbnVtYmVyIG9mIGNoYXJhY3RlcnMgbGVhZGluZyB1cCB0byBhIHBhcnRpY3VsYXIgc2V0IG9mIGNoYXJhY3RlcnMgb3IgZm9sbG93aW5nIGEgY2VydGFpbiBzZXQgb2YgY2hhcmFjdGVycy4KYGBge3NxbH0KLyogc2VsZWN0IGFsbCB0aGUgY29tcGFuaWVzIHdob3NlIG5hbWVzIHN0YXJ0IHdpdGggJ0MnICovClNFTEVDVCBuYW1lIApGUk9NIGFjY291bnRzCldIRVJFIG5hbWUgbGlrZSAnQyUnCgovKiBzZWxlY3QgQWxsIGNvbXBhbmllcyB3aG9zZSBuYW1lcyBjb250YWluIHRoZSBzdHJpbmcgJ29uZScgc29tZXdoZXJlIGluIHRoZSBuYW1lLiAqLwpTRUxFQ1QgbmFtZQpGUk9NIGFjY291bnRzCldIRVJFIG5hbWUgTElLRSAnJW9uZSUnOwpgYGAKCioqSU4qKiAtIFRoaXMgYWxsb3dzIHlvdSB0byBwZXJmb3JtIG9wZXJhdGlvbnMgc2ltaWxhciB0byB1c2luZyBXSEVSRSBhbmQgPSwgYnV0IGZvciBtb3JlIHRoYW4gb25lIGNvbmRpdGlvbi4gIFdlIGNhbiBjaGVjayBvbmUsIHR3byBvciBtYW55IGNvbHVtbiB2YWx1ZXMgZm9yIHdoaWNoIHdlIHdhbnQgdG8gcHVsbCBkYXRhLCBidXQgYWxsIHdpdGhpbiB0aGUgc2FtZSBxdWVyeS4gClRJUCAtIEluIG1vc3QgU1FMIGVudmlyb25tZW50cywgeW91IGNhbiB1c2Ugc2luZ2xlIG9yIGRvdWJsZSBxdW90YXRpb24gbWFya3MgLSBhbmQgeW91IG1heSBORUVEIHRvIHVzZSBkb3VibGUgcXVvdGF0aW9uIG1hcmtzIGlmIHlvdSBoYXZlIGFuIGFwb3N0cm9waGUgd2l0aGluIHRoZSB0ZXh0IHlvdSBhcmUgYXR0ZW1wdGluZyB0byBwdWxsLgpgYGB7c3FsfQpTRUxFQ1QgbmFtZSwgcHJpbWFyeV9wb2MsIHNhbGVzX3JlcF9pZApGUk9NIGFjY291bnRzCldIRVJFIG5hbWUgSU4gKCdXYWxtYXJ0JywgJ1RhcmdldCcsICdOb3Jkc3Ryb20nKTsKYGBgCioqTk9UKiogLSBUaGlzIGlzIHVzZWQgd2l0aCBJTiBhbmQgTElLRSB0byBzZWxlY3QgYWxsIG9mIHRoZSByb3dzIE5PVCBMSUtFIG9yIE5PVCBJTiBhIGNlcnRhaW4gY29uZGl0aW9uLgpgYGB7c3FsfQovKiBVc2UgdGhlIGFjY291bnRzIHRhYmxlIHRvIGZpbmQgdGhlIGFjY291bnQgbmFtZSwgcHJpbWFyeSBwb2MsIGFuZCBzYWxlcyByZXAgaWQgZm9yIGFsbCBzdG9yZXMgZXhjZXB0IFdhbG1hcnQsIFRhcmdldCwgYW5kIE5vcmRzdHJvbSAqLwpTRUxFQ1QgbmFtZSwgcHJpbWFyeV9wb2MsIHNhbGVzX3JlcF9pZApGUk9NIGFjY291bnRzCldIRVJFIG5hbWUgTk9UIElOICgnV2FsbWFydCcsICdUYXJnZXQnLCAnTm9yZHN0cm9tJyk7CmBgYAoKKipBTkQqKiAmICoqQkVUV0VFTioqIC0gVGhlc2UgYWxsb3cgeW91IHRvIGNvbWJpbmUgb3BlcmF0aW9ucyB3aGVyZSBhbGwgY29tYmluZWQgY29uZGl0aW9ucyBtdXN0IGJlIHRydWUuCiogVGhlIEFORCBvcGVyYXRvciBpcyB1c2VkIHdpdGhpbiBhIFdIRVJFIHN0YXRlbWVudCB0byBjb25zaWRlciBtb3JlIHRoYW4gb25lIGxvZ2ljYWwgY2xhdXNlIGF0IGEgdGltZS4gRWFjaCB0aW1lIHlvdSBsaW5rIGEgbmV3IHN0YXRlbWVudCB3aXRoIGFuIEFORCwgeW91IHdpbGwgbmVlZCB0byBzcGVjaWZ5IHRoZSBjb2x1bW4geW91IGFyZSBpbnRlcmVzdGVkIGluIGxvb2tpbmcgYXQuCiogU29tZXRpbWVzIHdlIGNhbiBtYWtlIGEgY2xlYW5lciBzdGF0ZW1lbnQgdXNpbmcgQkVUV0VFTiB0aGFuIHdlIGNhbiB1c2luZyBBTkQuIFBhcnRpY3VsYXJseSB0aGlzIGlzIHRydWUgd2hlbiB3ZSBhcmUgdXNpbmcgdGhlIHNhbWUgY29sdW1uIGZvciBkaWZmZXJlbnQgcGFydHMgb2Ygb3VyIEFORCBzdGF0ZW1lbnQuIGUuZy46CmBgYHtzcWx9Ci8qSW5zdGVhZCBvZiB3cml0aW5nIDogV0hFUkUgY29sdW1uID49IDYgQU5EIGNvbHVtbiA8PSAxMCB3ZSBjYW4gaW5zdGVhZCB3cml0ZSovIApXSEVSRSBjb2x1bW4gQkVUV0VFTiA2IEFORCAxMApgYGAKCmBgYHtzcWx9Ci8qIFdyaXRlIGEgcXVlcnkgdGhhdCByZXR1cm5zIGFsbCB0aGUgb3JkZXJzIHdoZXJlIHRoZSBzdGFuZGFyZF9xdHkgaXMgb3ZlciAxMDAwLCB0aGUgcG9zdGVyX3F0eSBpcyAwLCBhbmQgdGhlIGdsb3NzX3F0eSBpcyAwICovClNFTEVDVCAqCkZST00gb3JkZXJzCldIRVJFIHN0YW5kYXJkX3F0eSA+IDEwMDAgQU5EIHBvc3Rlcl9xdHkgPSAwIEFORCBnbG9zc19xdHkgPSAwOwoKLyogVXNpbmcgdGhlIGFjY291bnRzIHRhYmxlIGZpbmQgYWxsIHRoZSBjb21wYW5pZXMgd2hvc2UgbmFtZXMgZG8gbm90IHN0YXJ0IHdpdGggJ0MnIGFuZCBlbmQgd2l0aCAncycgKi8KU0VMRUNUIG5hbWUKRlJPTSBhY2NvdW50cwpXSEVSRSBuYW1lIE5PVCBMSUtFICdDJScgQU5EIG5hbWUgTElLRSAnJXMnOwoKLyogVXNlIHRoZSB3ZWJfZXZlbnRzIHRhYmxlIHRvIGZpbmQgYWxsIGluZm9ybWF0aW9uIHJlZ2FyZGluZyBpbmRpdmlkdWFscyB3aG8gd2VyZSBjb250YWN0ZWQgdmlhIG9yZ2FuaWMgb3IgYWR3b3JkcyBhbmQgc3RhcnRlZCB0aGVpciBhY2NvdW50IGF0IGFueSBwb2ludCBpbiAyMDE2IHNvcnRlZCBmcm9tIG5ld2VzdCB0byBvbGRlc3QgKi8gClNFTEVDVCAqCkZST00gd2ViX2V2ZW50cwpXSEVSRSBjaGFubmVsIElOICgnb3JnYW5pYycsICdhZHdvcmRzJykgQU5EIG9jY3VycmVkX2F0IEJFVFdFRU4gJzIwMTYtMDEtMDEnIEFORCAnMjAxNy0wMS0wMScKT1JERVIgQlkgb2NjdXJyZWRfYXQgREVTQzsKLyogIFlvdSB3aWxsIG5vdGljZSB0aGF0IHVzaW5nIEJFVFdFRU4gaXMgdHJpY2t5IGZvciBkYXRlcyEgV2hpbGUgQkVUV0VFTiBpcyBnZW5lcmFsbHkgaW5jbHVzaXZlIG9mIGVuZHBvaW50cywgaXQgYXNzdW1lcyB0aGUgdGltZSBpcyBhdCAwMDowMDowMCAoaS5lLiBtaWRuaWdodCkgZm9yIGRhdGVzLiBUaGlzIGlzIHRoZSByZWFzb24gd2h5IHdlIHNldCB0aGUgcmlnaHQtc2lkZSBlbmRwb2ludCBvZiB0aGUgcGVyaW9kIGF0ICcyMDE3LTAxLTAxJy4gKi8KYGBgCgoqKk9SKiogLSBUaGlzIGFsbG93IHlvdSB0byBjb21iaW5lIG9wZXJhdGlvbnMgd2hlcmUgYXQgbGVhc3Qgb25lIG9mIHRoZSBjb21iaW5lZCBjb25kaXRpb25zIG11c3QgYmUgdHJ1ZS4KU2ltaWxhciB0byB0aGUgQU5EIG9wZXJhdG9yLCB0aGUgT1Igb3BlcmF0b3IgY2FuIGNvbWJpbmUgbXVsdGlwbGUgc3RhdGVtZW50cy4gRWFjaCB0aW1lIHlvdSBsaW5rIGEgbmV3IHN0YXRlbWVudCB3aXRoIGFuIE9SLCB5b3Ugd2lsbCBuZWVkIHRvIHNwZWNpZnkgdGhlIGNvbHVtbiB5b3UgYXJlIGludGVyZXN0ZWQgaW4gbG9va2luZyBhdC4KYGBge3NxbH0KLyogRmluZCBsaXN0IG9mIG9yZGVycyBpZHMgd2hlcmUgZWl0aGVyIGdsb3NzX3F0eSBvciBwb3N0ZXJfcXR5IGlzIGdyZWF0ZXIgdGhhbiA0MDAwLiBPbmx5IGluY2x1ZGUgdGhlIGlkIGZpZWxkIGluIHRoZSByZXN1bHRpbmcgdGFibGUuICovClNFTEVDVCBpZApGUk9NIG9yZGVycwpXSEVSRSBnbG9zc19xdHkgPiA0MDAwIE9SIHBvc3Rlcl9xdHkgPiA0MDAwOwoKLyogV3JpdGUgYSBxdWVyeSB0aGF0IHJldHVybnMgYSBsaXN0IG9mIG9yZGVycyB3aGVyZSB0aGUgc3RhbmRhcmRfcXR5IGlzIHplcm8gYW5kIGVpdGhlciB0aGUgZ2xvc3NfcXR5IG9yIHBvc3Rlcl9xdHkgaXMgb3ZlciAxMDAwLiAqLwpTRUxFQ1QgKgpGUk9NIG9yZGVycwpXSEVSRSBzdGFuZGFyZF9xdHkgPSAwIEFORCAoZ2xvc3NfcXR5ID4gMTAwMCBPUiBwb3N0ZXJfcXR5ID4gMTAwMCk7CgovKiBGaW5kIGFsbCB0aGUgY29tcGFueSBuYW1lcyB0aGF0IHN0YXJ0IHdpdGggYSAnQycgb3IgJ1cnLCBhbmQgdGhlIHByaW1hcnkgY29udGFjdCBjb250YWlucyAnYW5hJyBvciAnQW5hJywgYnV0IGl0IGRvZXNuJ3QgY29udGFpbiAnZWFuYScqLwpTRUxFQ1QgKgpGUk9NIGFjY291bnRzCldIRVJFIChuYW1lIExJS0UgJ0MlJyBPUiBuYW1lIExJS0UgJ1clJykgCiAgICAgICAgICAgQU5EICgocHJpbWFyeV9wb2MgTElLRSAnJWFuYSUnIE9SIHByaW1hcnlfcG9jIExJS0UgJyVBbmElJykgCiAgICAgICAgICAgQU5EIHByaW1hcnlfcG9jIE5PVCBMSUtFICclZWFuYSUnKTsKYGBgCgojIyBTdW1tYXJ5IG9mIGJhc2ljIGNvbWFuZHM6CiFbXShpbWFnZXMvc3VtbWFyeV9iYXNpY19jb21tYW5kcy5wbmcpCgoqKioKCiMgSk9JTlMKIyMgSW5uZXIgam9pbnMgCk9ubHkgcmV0dXJuIHJvd3MgdGhhdCBhcHBlYXIgaW4gKipib3RoIHRhYmxlcyoqCiFbXShpbWFnZXMvRVJELnBuZykgCmUuZy4gVXNpbmcgRVJEOiBQdWxsIHN0YW5kYXJkX3F0eSwgZ2xvc3NfcXR5LCBhbmQgcG9zdGVyX3F0eSBmcm9tIHRoZSBvcmRlcnMgdGFibGUsIGFuZCB0aGUgd2Vic2l0ZSBhbmQgdGhlIHByaW1hcnlfcG9jIGZyb20gdGhlIGFjY291bnRzIHRhYmxlLgpgYGB7c3FsfQpTRUxFQ1Qgb3JkZXJzLnN0YW5kYXJkX3F0eSwgb3JkZXJzLmdsb3NzX3F0eSwgCiAgICAgICBvcmRlcnMucG9zdGVyX3F0eSwgIGFjY291bnRzLndlYnNpdGUsIAogICAgICAgYWNjb3VudHMucHJpbWFyeV9wb2MKRlJPTSBvcmRlcnMKSk9JTiBhY2NvdW50cwpPTiBvcmRlcnMuYWNjb3VudF9pZCA9IGFjY291bnRzLmlkCmBgYApOb3RpY2UgdGhhdCB3ZSBuZWVkIHRvIHNwZWNpZnkgZXZlcnkgdGFibGUgYSBjb2x1bW4gY29tZXMgZnJvbSBpbiB0aGUgU0VMRUNUIHN0YXRlbWVudC4KIyMjIEFMSUFTCldoZW4gd2UgSk9JTiB0YWJsZXMgdG9nZXRoZXIsIGl0IGlzIG5pY2UgdG8gZ2l2ZSBlYWNoIHRhYmxlIGFuIGFsaWFzLiBGcmVxdWVudGx5IGFuIGFsaWFzIGlzIGp1c3QgdGhlIGZpcnN0IGxldHRlciBvZiB0aGUgdGFibGUgbmFtZS4gCmBgYHtzcWx9CkZST00gdGFibGVuYW1lIHQxIApKT0lOIHRhYmxlbmFtZTIgdDIKYGBgCgojIyBRdWVzdGlvbnMKMS4gUHJvdmlkZSBhIHRhYmxlIGZvciBhbGwgKndlYl9ldmVudHMqIGFzc29jaWF0ZWQgd2l0aCBhY2NvdW50IG5hbWUgb2YgKldhbG1hcnQqLiBUaGVyZSBzaG91bGQgYmUgdGhyZWUgY29sdW1ucy4gQmUgc3VyZSB0byBpbmNsdWRlIHRoZSBwcmltYXJ5X3BvYywgdGltZSBvZiB0aGUgZXZlbnQsIGFuZCB0aGUgY2hhbm5lbCBmb3IgZWFjaCBldmVudC4gQWRkaXRpb25hbGx5LCB5b3UgbWlnaHQgY2hvb3NlIHRvIGFkZCBhIGZvdXJ0aCBjb2x1bW4gdG8gYXNzdXJlIG9ubHkgV2FsbWFydCBldmVudHMgd2VyZSBjaG9zZW4uIApgYGB7c3FsfQpTRUxFQ1QgYS5wcmltYXJ5X3BvYywgdy5vY2N1cnJlZF9hdCwgdy5jaGFubmVsLCBhLm5hbWUKRlJPTSB3ZWJfZXZlbnRzIHcKSk9JTiBhY2NvdW50cyBhCk9OIHcuYWNjb3VudF9pZCA9IGEuaWQKV0hFUkUgYS5uYW1lID0gJ1dhbG1hcnQnOwpgYGAKCjIuIFByb3ZpZGUgYSB0YWJsZSB0aGF0IHByb3ZpZGVzIHRoZSAqcmVnaW9uKiBmb3IgZWFjaCAqc2FsZXNfcmVwKiBhbG9uZyB3aXRoIHRoZWlyIGFzc29jaWF0ZWQgKmFjY291bnRzKi4gWW91ciBmaW5hbCB0YWJsZSBzaG91bGQgaW5jbHVkZSB0aHJlZSBjb2x1bW5zOiB0aGUgcmVnaW9uICpuYW1lKiwgdGhlIHNhbGVzIHJlcCAqbmFtZSosIGFuZCB0aGUgYWNjb3VudCAqbmFtZSouIFNvcnQgdGhlIGFjY291bnRzIGFscGhhYmV0aWNhbGx5IChBLVopIGFjY29yZGluZyB0byBhY2NvdW50IG5hbWUuIApgYGB7c3FsfQpTRUxFQ1Qgci5uYW1lIHJlZ2lvbiwgcy5uYW1lIHJlcCwgYS5uYW1lIGFjY291bnQKRlJPTSBzYWxlc19yZXBzIHMKSk9JTiByZWdpb24gcgpPTiBzLnJlZ2lvbl9pZCA9IHIuaWQKSk9JTiBhY2NvdW50cyBhCk9OIGEuc2FsZXNfcmVwX2lkID0gcy5pZApPUkRFUiBCWSBhLm5hbWU7CmBgYAoKMy4gUHJvdmlkZSB0aGUgKm5hbWUqIGZvciBlYWNoIHJlZ2lvbiBmb3IgZXZlcnkgKm9yZGVyKiwgYXMgd2VsbCBhcyB0aGUgYWNjb3VudCAqbmFtZSogYW5kIHRoZSAqdW5pdCBwcmljZSogdGhleSBwYWlkICh0b3RhbF9hbXRfdXNkL3RvdGFsKSBmb3IgdGhlIG9yZGVyLiBZb3VyIGZpbmFsIHRhYmxlIHNob3VsZCBoYXZlIDMgY29sdW1uczogKnJlZ2lvbiBuYW1lLCBhY2NvdW50IG5hbWUsIGFuZCB1bml0IHByaWNlKi4gQSBmZXcgYWNjb3VudHMgaGF2ZSAwIGZvciAqdG90YWwqLCBzbyBJIGRpdmlkZWQgYnkgKHRvdGFsICsgMC4wMSkgdG8gYXNzdXJlIG5vdCBkaXZpZGluZyBieSB6ZXJvLgpgYGB7c3FsfQpTRUxFQ1Qgci5uYW1lIHJlZ2lvbiwgYS5uYW1lIGFjY291bnQsIAogICAgICAgby50b3RhbF9hbXRfdXNkLyhvLnRvdGFsICsgMC4wMSkgdW5pdF9wcmljZQpGUk9NIHJlZ2lvbiByCkpPSU4gc2FsZXNfcmVwcyBzCk9OIHMucmVnaW9uX2lkID0gci5pZApKT0lOIGFjY291bnRzIGEKT04gYS5zYWxlc19yZXBfaWQgPSBzLmlkCkpPSU4gb3JkZXJzIG8KT04gby5hY2NvdW50X2lkID0gYS5pZDsKYGBgCgojIyBPdXRlciBqb2lucwpJZiB3ZSB3YW50IHRvIGluY2x1ZGUgZGF0YSB0aGF0IG9ubHkgZXhpc3RzIGluIG9uZSBvZiB0aGUgdGFibGVzIHdlIGFudCB0byBqb2luIHdlIGNhbiB1c2UgYW4gb3V0ZXIgam9pbgohW10oaW1hZ2VzL3R5cGVzX29mX2pvaW5zLnBuZykgCklmIHRoZXJlIGlzIG5vdCBtYXRjaGluZyBpbmZvcm1hdGlvbiBpbiB0aGUgSk9JTmVkIHRhYmxlLCB0aGVuIHlvdSB3aWxsIGhhdmUgY29sdW1ucyB3aXRoIGVtcHR5IGNlbGxzIHdpdGggYSBkYXRhIHR5cGUgY2FsbGVkIE5VTEwuCldyaXRpbmcgYSBMRUZUIGpvaW4gc3RhdGVtZW50OgohW10oaW1hZ2VzL2xlZnRfam9pbi5wbmcpIAohW10oaW1hZ2VzL2xlZnRfam9pbl9wdDIucG5nKSAKQ29tcGFyaW5nIExFRlQgYW5kIFJJR0hUIGpvaW46CiFbXShpbWFnZXMvY29tcGFyZV9sZWZ0X2FuZF9yaWdodF9qb2luLnBuZykgCkxFRlQgYW5kIFJJR0hUIGpvaW5zIGFyZSBlc3NlbnRpYWxseSBpbnRlciBjaGFuZ2VhYmxlIHNvIGl0cyBzdGFuZGFyZCB0byAqanVzdCB1c2UgIExFRlQgam9pbnMuKgoKIyMgSk9JTlMgYW5kIGZpbHRlcmluZwpJZiB5b3Ugd2FudGVkIHRvIGZpbHRlciB5b3VyIHJlc3VsdHMgc28gdGhhdCBpdCBvbmx5IHNob3dlZCBzYWxlcyBmcm9tIGEgcGFydGljdWxhciBzYWxlcyByZXAgeW91IGNvdWxkIHVzZSBXSEVSRSBidXQgaXTigJlzICoqYmV0dGVyIHRvIHVzZSBBTkQgYXMgcGFydCBvZiB0aGUgT04gc3RhdGVtZW50LioqCkEgc2ltcGxlIHJ1bGUgdG8gcmVtZW1iZXIgdGhpcyBpcyB0aGF0LCB3aGVuIHRoZSBkYXRhYmFzZSBleGVjdXRlcyB0aGlzIHF1ZXJ5LCBpdCBleGVjdXRlcyB0aGUgam9pbiBhbmQgZXZlcnl0aGluZyBpbiB0aGUgT04gY2xhdXNlIGZpcnN0LiBUaGluayBvZiB0aGlzIGFzIGJ1aWxkaW5nIHRoZSBuZXcgcmVzdWx0IHNldC4gVGhhdCByZXN1bHQgc2V0IGlzIHRoZW4gZmlsdGVyZWQgdXNpbmcgdGhlIFdIRVJFIGNsYXVzZS4KYGBge3NxbH0KU0VMRUNUIG9yZGVycy4qLCAKYWNjb3VudHMuKgpGUk9NIG9yZGVycwpMRUZUIEpPSU4gYWNjb3VudHMKT04gb3JkZXJzLmFjY291bnRfaWQgPSBhY2NvdW50cy5pZAovKiBVc2UgQU5EIGluIHN0ZWFkIG9mIFdIRVJFIGZvciBmaWx0ZXJpbmcgdGhpcyBMRUZUIEpPSU4gKi8KQU5EIGFjY291bnRzLnNhbGVzX3JlcF9pZCA9IDMyNSAKYGBgCgojIyBRdWVzdGlvbnMKMS4gUHJvdmlkZSBhIHRhYmxlIHRoYXQgcHJvdmlkZXMgdGhlIHJlZ2lvbiBmb3IgZWFjaCBzYWxlc19yZXAgYWxvbmcgd2l0aCB0aGVpciBhc3NvY2lhdGVkIGFjY291bnRzLiBUaGlzIHRpbWUgb25seSBmb3IgdGhlIE1pZHdlc3QgcmVnaW9uLiBZb3VyIGZpbmFsIHRhYmxlIHNob3VsZCBpbmNsdWRlIHRocmVlIGNvbHVtbnM6IHRoZSByZWdpb24gbmFtZSwgdGhlIHNhbGVzIHJlcCBuYW1lLCBhbmQgdGhlIGFjY291bnQgbmFtZS4gU29ydCB0aGUgYWNjb3VudHMgYWxwaGFiZXRpY2FsbHkgKEEtWikgYWNjb3JkaW5nIHRvIGFjY291bnQgbmFtZS4KYGBge3NxbH0KU0VMRUNUIHIubmFtZSByZWdpb24sIHMubmFtZSByZXAsIGEubmFtZSBhY2NvdW50CkZST00gc2FsZXNfcmVwcyBzCkpPSU4gcmVnaW9uIHIKT04gcy5yZWdpb25faWQgPSByLmlkCkpPSU4gYWNjb3VudHMgYQpPTiBhLnNhbGVzX3JlcF9pZCA9IHMuaWQKV0hFUkUgci5uYW1lID0gJ01pZHdlc3QnCk9SREVSIEJZIGEubmFtZTsKYGBgCgoyLiBQcm92aWRlIGEgdGFibGUgdGhhdCBwcm92aWRlcyB0aGUgcmVnaW9uIGZvciBlYWNoIHNhbGVzX3JlcCBhbG9uZyB3aXRoIHRoZWlyIGFzc29jaWF0ZWQgYWNjb3VudHMuIFRoaXMgdGltZSBvbmx5IGZvciBhY2NvdW50cyB3aGVyZSB0aGUgc2FsZXMgcmVwIGhhcyBhIGZpcnN0IG5hbWUgc3RhcnRpbmcgd2l0aCBTIGFuZCBpbiB0aGUgTWlkd2VzdCByZWdpb24uIFlvdXIgZmluYWwgdGFibGUgc2hvdWxkIGluY2x1ZGUgdGhyZWUgY29sdW1uczogdGhlIHJlZ2lvbiBuYW1lLCB0aGUgc2FsZXMgcmVwIG5hbWUsIGFuZCB0aGUgYWNjb3VudCBuYW1lLiBTb3J0IHRoZSBhY2NvdW50cyBhbHBoYWJldGljYWxseSAoQS1aKSBhY2NvcmRpbmcgdG8gYWNjb3VudCBuYW1lLiAKYGBge3NxbH0KU0VMRUNUIHIubmFtZSByZWdpb24sIHMubmFtZSByZXAsIGEubmFtZSBhY2NvdW50CkZST00gc2FsZXNfcmVwcyBzCkpPSU4gcmVnaW9uIHIKT04gcy5yZWdpb25faWQgPSByLmlkCkpPSU4gYWNjb3VudHMgYQpPTiBhLnNhbGVzX3JlcF9pZCA9IHMuaWQKV0hFUkUgci5uYW1lID0gJ01pZHdlc3QnIEFORCBzLm5hbWUgTElLRSAnUyUnCk9SREVSIEJZIGEubmFtZTsKYGBgCgozLiBQcm92aWRlIGEgdGFibGUgdGhhdCBwcm92aWRlcyB0aGUgcmVnaW9uIGZvciBlYWNoIHNhbGVzX3JlcCBhbG9uZyB3aXRoIHRoZWlyIGFzc29jaWF0ZWQgYWNjb3VudHMuIFRoaXMgdGltZSBvbmx5IGZvciBhY2NvdW50cyB3aGVyZSB0aGUgc2FsZXMgcmVwIGhhcyBhIGxhc3QgbmFtZSBzdGFydGluZyB3aXRoIEsgYW5kIGluIHRoZSBNaWR3ZXN0IHJlZ2lvbi4gWW91ciBmaW5hbCB0YWJsZSBzaG91bGQgaW5jbHVkZSB0aHJlZSBjb2x1bW5zOiB0aGUgcmVnaW9uIG5hbWUsIHRoZSBzYWxlcyByZXAgbmFtZSwgYW5kIHRoZSBhY2NvdW50IG5hbWUuIFNvcnQgdGhlIGFjY291bnRzIGFscGhhYmV0aWNhbGx5IChBLVopIGFjY29yZGluZyB0byBhY2NvdW50IG5hbWUuCmBgYHtzcWx9ClNFTEVDVCByLm5hbWUgcmVnaW9uLCBzLm5hbWUgcmVwLCBhLm5hbWUgYWNjb3VudApGUk9NIHNhbGVzX3JlcHMgcwpKT0lOIHJlZ2lvbiByCk9OIHMucmVnaW9uX2lkID0gci5pZApKT0lOIGFjY291bnRzIGEKT04gYS5zYWxlc19yZXBfaWQgPSBzLmlkCldIRVJFIHIubmFtZSA9ICdNaWR3ZXN0JyBBTkQgcy5uYW1lIExJS0UgJyUgSyUnCk9SREVSIEJZIGEubmFtZTsKYGBgCgo0LiBQcm92aWRlIHRoZSBuYW1lIGZvciBlYWNoIHJlZ2lvbiBmb3IgZXZlcnkgb3JkZXIsIGFzIHdlbGwgYXMgdGhlIGFjY291bnQgbmFtZSBhbmQgdGhlIHVuaXQgcHJpY2UgdGhleSBwYWlkICh0b3RhbF9hbXRfdXNkL3RvdGFsKSBmb3IgdGhlIG9yZGVyLiBIb3dldmVyLCB5b3Ugc2hvdWxkIG9ubHkgcHJvdmlkZSB0aGUgcmVzdWx0cyBpZiB0aGUgc3RhbmRhcmQgb3JkZXIgcXVhbnRpdHkgZXhjZWVkcyAxMDAuIFlvdXIgZmluYWwgdGFibGUgc2hvdWxkIGhhdmUgMyBjb2x1bW5zOiByZWdpb24gbmFtZSwgYWNjb3VudCBuYW1lLCBhbmQgdW5pdCBwcmljZS4gSW4gb3JkZXIgdG8gYXZvaWQgYSBkaXZpc2lvbiBieSB6ZXJvIGVycm9yLCBhZGRpbmcgLjAxIHRvIHRoZSBkZW5vbWluYXRvciBoZXJlIGlzIGhlbHBmdWwgdG90YWxfYW10X3VzZC8odG90YWwrMC4wMSkuIApgYGB7c3FsfQpTRUxFQ1Qgci5uYW1lIHJlZ2lvbiwgYS5uYW1lIGFjY291bnQsIG8udG90YWxfYW10X3VzZC8oby50b3RhbCArIDAuMDEpIHVuaXRfcHJpY2UKRlJPTSByZWdpb24gcgpKT0lOIHNhbGVzX3JlcHMgcwpPTiBzLnJlZ2lvbl9pZCA9IHIuaWQKSk9JTiBhY2NvdW50cyBhCk9OIGEuc2FsZXNfcmVwX2lkID0gcy5pZApKT0lOIG9yZGVycyBvCk9OIG8uYWNjb3VudF9pZCA9IGEuaWQKV0hFUkUgby5zdGFuZGFyZF9xdHkgPiAxMDA7CmBgYAoKNS4gUHJvdmlkZSB0aGUgbmFtZSBmb3IgZWFjaCByZWdpb24gZm9yIGV2ZXJ5IG9yZGVyLCBhcyB3ZWxsIGFzIHRoZSBhY2NvdW50IG5hbWUgYW5kIHRoZSB1bml0IHByaWNlIHRoZXkgcGFpZCAodG90YWxfYW10X3VzZC90b3RhbCkgZm9yIHRoZSBvcmRlci4gSG93ZXZlciwgeW91IHNob3VsZCBvbmx5IHByb3ZpZGUgdGhlIHJlc3VsdHMgaWYgdGhlIHN0YW5kYXJkIG9yZGVyIHF1YW50aXR5IGV4Y2VlZHMgMTAwIGFuZCB0aGUgcG9zdGVyIG9yZGVyIHF1YW50aXR5IGV4Y2VlZHMgNTAuIFlvdXIgZmluYWwgdGFibGUgc2hvdWxkIGhhdmUgMyBjb2x1bW5zOiByZWdpb24gbmFtZSwgYWNjb3VudCBuYW1lLCBhbmQgdW5pdCBwcmljZS4gU29ydCBmb3IgdGhlIHNtYWxsZXN0IHVuaXQgcHJpY2UgZmlyc3QuIEluIG9yZGVyIHRvIGF2b2lkIGEgZGl2aXNpb24gYnkgemVybyBlcnJvciwgYWRkaW5nIC4wMSB0byB0aGUgZGVub21pbmF0b3IgaGVyZSBpcyBoZWxwZnVsICh0b3RhbF9hbXRfdXNkLyh0b3RhbCswLjAxKS4gCmBgYHtzcWx9ClNFTEVDVCByLm5hbWUgcmVnaW9uLCBhLm5hbWUgYWNjb3VudCwgby50b3RhbF9hbXRfdXNkLyhvLnRvdGFsICsgMC4wMSkgdW5pdF9wcmljZQpGUk9NIHJlZ2lvbiByCkpPSU4gc2FsZXNfcmVwcyBzCk9OIHMucmVnaW9uX2lkID0gci5pZApKT0lOIGFjY291bnRzIGEKT04gYS5zYWxlc19yZXBfaWQgPSBzLmlkCkpPSU4gb3JkZXJzIG8KT04gby5hY2NvdW50X2lkID0gYS5pZApXSEVSRSBvLnN0YW5kYXJkX3F0eSA+IDEwMCBBTkQgby5wb3N0ZXJfcXR5ID4gNTAKT1JERVIgQlkgdW5pdF9wcmljZTsKYGBgCgo2LiBQcm92aWRlIHRoZSBuYW1lIGZvciBlYWNoIHJlZ2lvbiBmb3IgZXZlcnkgb3JkZXIsIGFzIHdlbGwgYXMgdGhlIGFjY291bnQgbmFtZSBhbmQgdGhlIHVuaXQgcHJpY2UgdGhleSBwYWlkICh0b3RhbF9hbXRfdXNkL3RvdGFsKSBmb3IgdGhlIG9yZGVyLiBIb3dldmVyLCB5b3Ugc2hvdWxkIG9ubHkgcHJvdmlkZSB0aGUgcmVzdWx0cyBpZiB0aGUgc3RhbmRhcmQgb3JkZXIgcXVhbnRpdHkgZXhjZWVkcyAxMDAgYW5kIHRoZSBwb3N0ZXIgb3JkZXIgcXVhbnRpdHkgZXhjZWVkcyA1MC4gWW91ciBmaW5hbCB0YWJsZSBzaG91bGQgaGF2ZSAzIGNvbHVtbnM6IHJlZ2lvbiBuYW1lLCBhY2NvdW50IG5hbWUsIGFuZCB1bml0IHByaWNlLiBTb3J0IGZvciB0aGUgbGFyZ2VzdCB1bml0IHByaWNlIGZpcnN0LiBJbiBvcmRlciB0byBhdm9pZCBhIGRpdmlzaW9uIGJ5IHplcm8gZXJyb3IsIGFkZGluZyAuMDEgdG8gdGhlIGRlbm9taW5hdG9yIGhlcmUgaXMgaGVscGZ1bCAodG90YWxfYW10X3VzZC8odG90YWwrMC4wMSkuIApgYGB7c3FsfQpTRUxFQ1Qgci5uYW1lIHJlZ2lvbiwgYS5uYW1lIGFjY291bnQsIG8udG90YWxfYW10X3VzZC8oby50b3RhbCArIDAuMDEpIHVuaXRfcHJpY2UKRlJPTSByZWdpb24gcgpKT0lOIHNhbGVzX3JlcHMgcwpPTiBzLnJlZ2lvbl9pZCA9IHIuaWQKSk9JTiBhY2NvdW50cyBhCk9OIGEuc2FsZXNfcmVwX2lkID0gcy5pZApKT0lOIG9yZGVycyBvCk9OIG8uYWNjb3VudF9pZCA9IGEuaWQKV0hFUkUgby5zdGFuZGFyZF9xdHkgPiAxMDAgQU5EIG8ucG9zdGVyX3F0eSA+IDUwCk9SREVSIEJZIHVuaXRfcHJpY2UgREVTQzsKYGBgCgo3LiBXaGF0IGFyZSB0aGUgZGlmZmVyZW50IGNoYW5uZWxzIHVzZWQgYnkgYWNjb3VudCBpZCAxMDAxPyBZb3VyIGZpbmFsIHRhYmxlIHNob3VsZCBoYXZlIG9ubHkgMiBjb2x1bW5zOiBhY2NvdW50IG5hbWUgYW5kIHRoZSBkaWZmZXJlbnQgY2hhbm5lbHMuIFlvdSBjYW4gdHJ5IFNFTEVDVCBESVNUSU5DVCB0byBuYXJyb3cgZG93biB0aGUgcmVzdWx0cyB0byBvbmx5IHRoZSB1bmlxdWUgdmFsdWVzLgpgYGB7c3FsfQpTRUxFQ1QgRElTVElOQ1QgYS5uYW1lLCB3LmNoYW5uZWwKRlJPTSBhY2NvdW50cyBhClJJR0hUIEpPSU4gd2ViX2V2ZW50cyB3Ck9OIGEuaWQgPSB3LmFjY291bnRfaWQKV0hFUkUgYS5pZCA9ICcxMDAxJzsKYGBgCgo4LiBGaW5kIGFsbCB0aGUgb3JkZXJzIHRoYXQgb2NjdXJyZWQgaW4gMjAxNS4gWW91ciBmaW5hbCB0YWJsZSBzaG91bGQgaGF2ZSA0IGNvbHVtbnM6IG9jY3VycmVkX2F0LCBhY2NvdW50IG5hbWUsIG9yZGVyIHRvdGFsLCBhbmQgb3JkZXIgdG90YWxfYW10X3VzZC4gCmBgYHtzcWx9ClNFTEVDVCBvLm9jY3VycmVkX2F0LCBhLm5hbWUsIG8udG90YWwsIG8udG90YWxfYW10X3VzZApGUk9NIGFjY291bnRzIGEKSk9JTiBvcmRlcnMgbwpPTiBvLmFjY291bnRfaWQgPSBhLmlkCldIRVJFIG8ub2NjdXJyZWRfYXQgQkVUV0VFTiAnMDEtMDEtMjAxNScgQU5EICcwMS0wMS0yMDE2JwpPUkRFUiBCWSBvLm9jY3VycmVkX2F0IERFU0M7CmBgYAoKKioqCgojIEFHR1JFR0FUSU9OUwoKIyMgTnVsbCB2YWx1ZXMKV2hlbiBpZGVudGlmeWluZyBOVUxMcyBpbiBhIFdIRVJFIGNsYXVzZSwgd2Ugd3JpdGUgSVMgTlVMTCBvciBJUyBOT1QgTlVMTC4gV2UgZG9uJ3QgdXNlID0sIGJlY2F1c2UgTlVMTCBpc24ndCBjb25zaWRlcmVkIGEgdmFsdWUgaW4gU1FMLgoKIyMgQ09VTlQKQ291bnQgdGhlIG51bWJlciBvZiByb3dzIGluIGEgdGFibGUKYGBge3NxbH0KU0VMRUNUIENPVU5UKCopCkZST00gYWNjb3VudHM7CmBgYApDT1VOVCAqZG9lcyBub3QqIGNvbnNpZGVyIHJvd3MgdGhhdCBoYXZlICpOVUxMKiB2YWx1ZXMuIFRoZXJlZm9yZSwgdGhpcyBjYW4gYmUgdXNlZnVsIGZvciBxdWlja2x5IGlkZW50aWZ5aW5nIHdoaWNoIHJvd3MgaGF2ZSBtaXNzaW5nIGRhdGEuCgojIyBTVU0KVW5saWtlIENPVU5ULCB5b3UgY2FuIG9ubHkgdXNlIFNVTSBvbiBudW1lcmljIGNvbHVtbnMuIEhvd2V2ZXIsIFNVTSB3aWxsIGlnbm9yZSBOVUxMIHZhbHVlcyBhbmQgdHJlYXQgdGhlbSBhcyAwLgoKIyMjIFF1ZXN0aW9ucwoxLiBGaW5kIHRoZSB0b3RhbCBhbW91bnQgb2YgcG9zdGVyX3F0eSBwYXBlciBvcmRlcmVkIGluIHRoZSBvcmRlcnMgdGFibGUuCmBgYHtzcWx9ClNFTEVDVCBTVU0ocG9zdGVyX3F0eSkgQVMgdG90YWxfcG9zdGVyX3NhbGVzCkZST00gb3JkZXJzOwpgYGAKCjIuIEZpbmQgdGhlIHRvdGFsIGFtb3VudCBvZiBzdGFuZGFyZF9xdHkgcGFwZXIgb3JkZXJlZCBpbiB0aGUgb3JkZXJzIHRhYmxlLgpgYGB7c3FsfQpTRUxFQ1QgU1VNKHN0YW5kYXJkX3F0eSkgQVMgdG90YWxfc3RhbmRhcmRfc2FsZXMKRlJPTSBvcmRlcnM7CmBgYAoKMy4gRmluZCB0aGUgdG90YWwgZG9sbGFyIGFtb3VudCBvZiBzYWxlcyB1c2luZyB0aGUgdG90YWxfYW10X3VzZCBpbiB0aGUgb3JkZXJzIHRhYmxlLgpgYGB7c3FsfQpTRUxFQ1QgU1VNKHRvdGFsX2FtdF91c2QpIEFTIHRvdGFsX2RvbGxhcl9zYWxlcwpGUk9NIG9yZGVyczsKYGBgCgo0LiBGaW5kIHRoZSB0b3RhbCBhbW91bnQgc3BlbnQgb24gc3RhbmRhcmRfYW10X3VzZCBhbmQgZ2xvc3NfYW10X3VzZCBwYXBlciBmb3IgZWFjaCBvcmRlciBpbiB0aGUgb3JkZXJzIHRhYmxlLiBUaGlzIHNob3VsZCBnaXZlIGEgZG9sbGFyIGFtb3VudCBmb3IgZWFjaCBvcmRlciBpbiB0aGUgdGFibGUuCipOb3RpY2UsIHRoaXMgc29sdXRpb24gZGlkIG5vdCB1c2UgYW4gYWdncmVnYXRlLioKYGBge3NxbH0KU0VMRUNUIHN0YW5kYXJkX2FtdF91c2QgKyBnbG9zc19hbXRfdXNkIEFTIHRvdGFsX3N0YW5kYXJkX2dsb3NzCkZST00gb3JkZXJzOwpgYGAKCjUuIEZpbmQgdGhlIHN0YW5kYXJkX2FtdF91c2QgcGVyIHVuaXQgb2Ygc3RhbmRhcmRfcXR5IHBhcGVyLiBZb3VyIHNvbHV0aW9uIHNob3VsZCB1c2UgYm90aCBhbiBhZ2dyZWdhdGlvbiBhbmQgYSBtYXRoZW1hdGljYWwgb3BlcmF0b3IuIAoqTm90aWNlLCB0aGlzIHNvbHV0aW9uIHVzZWQgYm90aCBhbiBhZ2dyZWdhdGUgYW5kIG91ciBtYXRoZW1hdGljYWwgb3BlcmF0b3JzKgpgYGB7c3FsfQpTRUxFQ1QgU1VNKHN0YW5kYXJkX2FtdF91c2QpL1NVTShzdGFuZGFyZF9xdHkpIEFTIHN0YW5kYXJkX3ByaWNlX3Blcl91bml0CkZST00gb3JkZXJzOwpgYGAKCiMjIE1JTiBhbmQgTUFYCk1JTiBhbmQgTUFYIGFyZSBhZ2dyZWdhdG9ycyB0aGF0IGlnbm9yZSBOVUxMIHZhbHVlcy4KRnVuY3Rpb25hbGx5LCBNSU4gYW5kIE1BWCBhcmUgc2ltaWxhciB0byBDT1VOVCBpbiB0aGF0IHRoZXkgY2FuIGJlIHVzZWQgb24gbm9uLW51bWVyaWNhbCBjb2x1bW5zLiBEZXBlbmRpbmcgb24gdGhlIGNvbHVtbiB0eXBlLCBNSU4gd2lsbCByZXR1cm4gdGhlIGxvd2VzdCBudW1iZXIsIGVhcmxpZXN0IGRhdGUsIG9yIG5vbi1udW1lcmljYWwgdmFsdWUgYXMgZWFybHkgaW4gdGhlIGFscGhhYmV0IGFzIHBvc3NpYmxlLiBBcyB5b3UgbWlnaHQgc3VzcGVjdCwgTUFYIGRvZXMgdGhlIG9wcG9zaXRl4oCUaXQgcmV0dXJucyB0aGUgaGlnaGVzdCBudW1iZXIsIHRoZSBsYXRlc3QgZGF0ZSwgb3IgdGhlIG5vbi1udW1lcmljYWwgdmFsdWUgY2xvc2VzdCBhbHBoYWJldGljYWxseSB0byDigJxaLuKAnQoKIyMjIFF1ZXN0aW9ucwoxLiBXaGVuIHdhcyB0aGUgZWFybGllc3Qgb3JkZXIgZXZlciBwbGFjZWQ/CmBgYHtzcWx9ClNFTEVDVCBNSU4ob2NjdXJyZWRfYXQpIApGUk9NIG9yZGVyczsKYGBgClRyeSBwZXJmb3JtaW5nIHRoZSBzYW1lIHF1ZXJ5IGFzIGluIHF1ZXN0aW9uIDEgd2l0aG91dCB1c2luZyBhbiBhZ2dyZWdhdGlvbiBmdW5jdGlvbi4gCmBgYHtzcWx9ClNFTEVDVCBvY2N1cnJlZF9hdCAKRlJPTSBvcmRlcnMgCk9SREVSIEJZIG9jY3VycmVkX2F0CkxJTUlUIDE7CmBgYAoKMi4gV2hlbiBkaWQgdGhlIG1vc3QgcmVjZW50IChsYXRlc3QpIHdlYl9ldmVudCBvY2N1cj8KYGBge3NxbH0KU0VMRUNUIE1BWChvY2N1cnJlZF9hdCkKRlJPTSB3ZWJfZXZlbnRzOwpgYGAKVHJ5IHRvIHBlcmZvcm0gdGhlIHJlc3VsdCBvZiB0aGUgcHJldmlvdXMgcXVlcnkgd2l0aG91dCB1c2luZyBhbiBhZ2dyZWdhdGlvbiBmdW5jdGlvbi4KYGBge3NxbH0KU0VMRUNUIG9jY3VycmVkX2F0CkZST00gd2ViX2V2ZW50cwpPUkRFUiBCWSBvY2N1cnJlZF9hdCBERVNDCkxJTUlUIDE7CmBgYAoKIyMgQVZHCkFWRyByZXR1cm5zIHRoZSBtZWFuIG9mIHRoZSBkYXRhIC0gdGhhdCBpcyB0aGUgc3VtIG9mIGFsbCBvZiB0aGUgdmFsdWVzIGluIHRoZSBjb2x1bW4gZGl2aWRlZCBieSB0aGUgbnVtYmVyIG9mIHZhbHVlcyBpbiBhIGNvbHVtbi4KVGhpcyBhZ2dyZWdhdGUgZnVuY3Rpb24gaWdub3JlcyB0aGUgTlVMTCB2YWx1ZXMgaW4gYm90aCB0aGUgbnVtZXJhdG9yIGFuZCB0aGUgZGVub21pbmF0b3IuCgojIyMgUXVlc3Rpb25zCjEuIEZpbmQgdGhlIG1lYW4gKEFWRVJBR0UpIGFtb3VudCBzcGVudCBwZXIgb3JkZXIgb24gZWFjaCBwYXBlciB0eXBlLCBhcyB3ZWxsIGFzIHRoZSBtZWFuIGFtb3VudCBvZiBlYWNoIHBhcGVyIHR5cGUgcHVyY2hhc2VkIHBlciBvcmRlci4gWW91ciBmaW5hbCBhbnN3ZXIgc2hvdWxkIGhhdmUgNiB2YWx1ZXMgLSBvbmUgZm9yIGVhY2ggcGFwZXIgdHlwZSBmb3IgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHNhbGVzLCBhcyB3ZWxsIGFzIHRoZSBhdmVyYWdlIGFtb3VudC4KYGBge3NxbH0KU0VMRUNUIEFWRyhzdGFuZGFyZF9xdHkpIG1lYW5fc3RhbmRhcmQsIEFWRyhnbG9zc19xdHkpIG1lYW5fZ2xvc3MsIAogICAgICAgICAgIEFWRyhwb3N0ZXJfcXR5KSBtZWFuX3Bvc3RlciwgQVZHKHN0YW5kYXJkX2FtdF91c2QpIG1lYW5fc3RhbmRhcmRfdXNkLCAKICAgICAgICAgICBBVkcoZ2xvc3NfYW10X3VzZCkgbWVhbl9nbG9zc191c2QsIEFWRyhwb3N0ZXJfYW10X3VzZCkgbWVhbl9wb3N0ZXJfdXNkCkZST00gb3JkZXJzOwpgYGAKCjIuIFdoYXQgaXMgdGhlIE1FRElBTiB0b3RhbF91c2Qgc3BlbnQgb24gYWxsIG9yZGVycz8gTm90ZSwgdGhpcyBpcyBtb3JlIGFkdmFuY2VkIHRoYW4gdGhlIHRvcGljcyB3ZSBoYXZlIGNvdmVyZWQgdGh1cyBmYXIgdG8gYnVpbGQgYSBnZW5lcmFsIHNvbHV0aW9uLCBidXQgd2UgY2FuIGhhcmQgY29kZSBhIHNvbHV0aW9uIGluIHRoZSBmb2xsb3dpbmcgd2F5LgpgYGAge3NxbH0KU0VMRUNUICoKRlJPTSAoU0VMRUNUIHRvdGFsX2FtdF91c2QKICAgICAgRlJPTSBvcmRlcnMKICAgICAgT1JERVIgQlkgdG90YWxfYW10X3VzZAogICAgICBMSU1JVCAzNDU3KSBBUyBUYWJsZTEKT1JERVIgQlkgdG90YWxfYW10X3VzZCBERVNDCkxJTUlUIDI7CmBgYApTaW5jZSB0aGVyZSBhcmUgNjkxMiBvcmRlcnMgLSB3ZSB3YW50IHRoZSBhdmVyYWdlIG9mIHRoZSAzNDU3IGFuZCAzNDU2IG9yZGVyIGFtb3VudHMgd2hlbiBvcmRlcmVkLiBUaGlzIGlzIHRoZSBhdmVyYWdlIG9mIDI0ODMuMTYgYW5kIDI0ODIuNTUuIFRoaXMgZ2l2ZXMgdGhlIG1lZGlhbiBvZiAyNDgyLjg1NS4gVGhpcyBvYnZpb3VzbHkgaXNuJ3QgYW4gaWRlYWwgd2F5IHRvIGNvbXB1dGUuIElmIHdlIG9idGFpbiBuZXcgb3JkZXJzLCB3ZSB3b3VsZCBoYXZlIHRvIGNoYW5nZSB0aGUgbGltaXQuIFNRTCBkaWRuJ3QgZXZlbiBjYWxjdWxhdGUgdGhlIG1lZGlhbiBmb3IgdXMuIFRoZSBhYm92ZSB1c2VkIGEgU1VCUVVFUlksIGJ1dCB5b3UgY291bGQgdXNlIGFueSBtZXRob2QgdG8gZmluZCB0aGUgdHdvIG5lY2Vzc2FyeSB2YWx1ZXMsIGFuZCB0aGVuIHlvdSBqdXN0IG5lZWQgdGhlIGF2ZXJhZ2Ugb2YgdGhlbS4KCiMjIEdST1VQIEJZCkdST1VQIEJZIGNhbiBiZSB1c2VkIHRvIGFnZ3JlZ2F0ZSBkYXRhIHdpdGhpbiBzdWJzZXRzIG9mIHRoZSBkYXRhLiBGb3IgZXhhbXBsZSwgZ3JvdXBpbmcgZm9yIGRpZmZlcmVudCBhY2NvdW50cywgZGlmZmVyZW50IHJlZ2lvbnMsIG9yIGRpZmZlcmVudCBzYWxlcyByZXByZXNlbnRhdGl2ZXMuCkFueSBjb2x1bW4gaW4gdGhlIFNFTEVDVCBzdGF0ZW1lbnQgdGhhdCBpcyBub3Qgd2l0aGluIGFuIGFnZ3JlZ2F0b3IgbXVzdCBiZSBpbiB0aGUgR1JPVVAgQlkgY2xhdXNlLgpUaGUgR1JPVVAgQlkgYWx3YXlzIGdvZXMgYmV0d2VlbiBXSEVSRSBhbmQgT1JERVIgQlkuCk9SREVSIEJZIHdvcmtzIGxpa2UgU09SVCBpbiBzcHJlYWRzaGVldCBzb2Z0d2FyZS4KU1FMIGV2YWx1YXRlcyB0aGUgYWdncmVnYXRpb25zIGJlZm9yZSB0aGUgTElNSVQgY2xhdXNlLiBJZiB5b3UgZG9u4oCZdCBncm91cCBieSBhbnkgY29sdW1ucywgeW914oCZbGwgZ2V0IGEgMS1yb3cgcmVzdWx04oCUbm8gcHJvYmxlbSB0aGVyZS4gSWYgeW91IGdyb3VwIGJ5IGEgY29sdW1uIHdpdGggZW5vdWdoIHVuaXF1ZSB2YWx1ZXMgdGhhdCBpdCBleGNlZWRzIHRoZSBMSU1JVCBudW1iZXIsIHRoZSBhZ2dyZWdhdGVzIHdpbGwgYmUgY2FsY3VsYXRlZCwgYW5kIHRoZW4gc29tZSByb3dzIHdpbGwgc2ltcGx5IGJlIG9taXR0ZWQgZnJvbSB0aGUgcmVzdWx0cy4KVGhpcyBpcyBhIG5pY2Ugd2F5IHRvIGRvIHRoaW5ncyBiZWNhdXNlIHlvdSBrbm93IHlvdeKAmXJlIGdvaW5nIHRvIGdldCB0aGUgY29ycmVjdCBhZ2dyZWdhdGVzLiBJZiBTUUwgY3V0cyB0aGUgdGFibGUgZG93biB0byAxMDAgcm93cywgdGhlbiBwZXJmb3JtZWQgdGhlIGFnZ3JlZ2F0aW9ucywgeW91ciByZXN1bHRzIHdvdWxkIGJlIHN1YnN0YW50aWFsbHkgZGlmZmVyZW50LgoKTm90ZTogWW91IGNhbiByZWZlcmVuY2UgdGhlIGNvbHVtbnMgaW4geW91ciBzZWxlY3Qgc3RhdGVtZW50IGluIEdST1VQIEJZIGFuZCBPUkRFUiBCWSBjbGF1c2VzIHdpdGggbnVtYmVycyB0aGF0IGZvbGxvdyB0aGUgb3JkZXIgdGhleSBhcHBlYXIgaW4gdGhlIHNlbGVjdCBzdGF0ZW1lbnQuIEZvciBleGFtcGxlCmBgYHtzcWx9ClNFTEVDVCBzdGFuZGFyZF9xdHksIENPVU5UKCopCgpGUk9NIG9yZGVycwoKR1JPVVAgQlkgMSAvKih0aGlzIDEgcmVmZXJzIHRvIHN0YW5kYXJkX3F0eSBzaW5jZSBpdCBpcyB0aGUgZmlyc3Qgb2YgdGhlIGNvbHVtbnMgaW5jbHVkZWQgaW4gdGhlIHNlbGVjdCBzdGF0ZW1lbnQpKi8KCk9SREVSIEJZIDEgLyoodGhpcyAxIHJlZmVycyB0byBzdGFuZGFyZF9xdHkgc2luY2UgaXQgaXMgdGhlIGZpcnN0IG9mIHRoZSBjb2x1bW5zIGluY2x1ZGVkIGluIHRoZSBzZWxlY3Qgc3RhdGVtZW50KSovCmBgYAoKIyMjIFF1ZXN0aW9ucwoxLiBXaGljaCBhY2NvdW50IChieSBuYW1lKSBwbGFjZWQgdGhlIGVhcmxpZXN0IG9yZGVyPyBZb3VyIHNvbHV0aW9uIHNob3VsZCBoYXZlIHRoZSBhY2NvdW50IG5hbWUgYW5kIHRoZSBkYXRlIG9mIHRoZSBvcmRlci4KYGBge3NxbH0KU0VMRUNUIGEubmFtZSwgby5vY2N1cnJlZF9hdApGUk9NIGFjY291bnRzIGEKSk9JTiBvcmRlcnMgbwpPTiBhLmlkID0gby5hY2NvdW50X2lkCk9SREVSIEJZIG9jY3VycmVkX2F0CkxJTUlUIDE7CmBgYAoKMi4gRmluZCB0aGUgdG90YWwgc2FsZXMgaW4gdXNkIGZvciBlYWNoIGFjY291bnQuIFlvdSBzaG91bGQgaW5jbHVkZSB0d28gY29sdW1ucyAtIHRoZSB0b3RhbCBzYWxlcyBmb3IgZWFjaCBjb21wYW55J3Mgb3JkZXJzIGluIHVzZCBhbmQgdGhlIGNvbXBhbnkgbmFtZS4KYGBge3NxbH0KU0VMRUNUIGEubmFtZSwgU1VNKHRvdGFsX2FtdF91c2QpIHRvdGFsX3NhbGVzCkZST00gb3JkZXJzIG8KSk9JTiBhY2NvdW50cyBhCk9OIGEuaWQgPSBvLmFjY291bnRfaWQKR1JPVVAgQlkgYS5uYW1lOwpgYGAKCjMuIFZpYSB3aGF0IGNoYW5uZWwgZGlkIHRoZSBtb3N0IHJlY2VudCAobGF0ZXN0KSB3ZWJfZXZlbnQgb2NjdXIsIHdoaWNoIGFjY291bnQgd2FzIGFzc29jaWF0ZWQgd2l0aCB0aGlzIHdlYl9ldmVudD8gWW91ciBxdWVyeSBzaG91bGQgcmV0dXJuIG9ubHkgdGhyZWUgdmFsdWVzIC0gdGhlIGRhdGUsIGNoYW5uZWwsIGFuZCBhY2NvdW50IG5hbWUuCmBgYHtzcWx9ClNFTEVDVCB3Lm9jY3VycmVkX2F0LCB3LmNoYW5uZWwsIGEubmFtZQpGUk9NIHdlYl9ldmVudHMgdwpKT0lOIGFjY291bnRzIGEKT04gdy5hY2NvdW50X2lkID0gYS5pZCAKT1JERVIgQlkgdy5vY2N1cnJlZF9hdCBERVNDCkxJTUlUIDE7CmBgYAoKNC4gRmluZCB0aGUgdG90YWwgbnVtYmVyIG9mIHRpbWVzIGVhY2ggdHlwZSBvZiBjaGFubmVsIGZyb20gdGhlIHdlYl9ldmVudHMgd2FzIHVzZWQuIFlvdXIgZmluYWwgdGFibGUgc2hvdWxkIGhhdmUgdHdvIGNvbHVtbnMgLSB0aGUgY2hhbm5lbCBhbmQgdGhlIG51bWJlciBvZiB0aW1lcyB0aGUgY2hhbm5lbCB3YXMgdXNlZC4KYGBge3NxbH0KU0VMRUNUIHcuY2hhbm5lbCwgQ09VTlQoKikKRlJPTSB3ZWJfZXZlbnRzIHcKR1JPVVAgQlkgdy5jaGFubmVsCmBgYAoKNS4gV2hvIHdhcyB0aGUgcHJpbWFyeSBjb250YWN0IGFzc29jaWF0ZWQgd2l0aCB0aGUgZWFybGllc3Qgd2ViX2V2ZW50PwpgYGB7c3FsfQpTRUxFQ1QgYS5wcmltYXJ5X3BvYwpGUk9NIHdlYl9ldmVudHMgdwpKT0lOIGFjY291bnRzIGEKT04gYS5pZCA9IHcuYWNjb3VudF9pZApPUkRFUiBCWSB3Lm9jY3VycmVkX2F0CkxJTUlUIDE7CmBgYAoKNi4gV2hhdCB3YXMgdGhlIHNtYWxsZXN0IG9yZGVyIHBsYWNlZCBieSBlYWNoIGFjY291bnQgaW4gdGVybXMgb2YgdG90YWwgdXNkLiBQcm92aWRlIG9ubHkgdHdvIGNvbHVtbnMgLSB0aGUgYWNjb3VudCBuYW1lIGFuZCB0aGUgdG90YWwgdXNkLiBPcmRlciBmcm9tIHNtYWxsZXN0IGRvbGxhciBhbW91bnRzIHRvIGxhcmdlc3QuCmBgYHtzcWx9IApTRUxFQ1QgYS5uYW1lLCBNSU4odG90YWxfYW10X3VzZCkgc21hbGxlc3Rfb3JkZXIKRlJPTSBhY2NvdW50cyBhCkpPSU4gb3JkZXJzIG8KT04gYS5pZCA9IG8uYWNjb3VudF9pZApHUk9VUCBCWSBhLm5hbWUKT1JERVIgQlkgc21hbGxlc3Rfb3JkZXI7CmBgYApTb3J0IG9mIHN0cmFuZ2Ugd2UgaGF2ZSBhIGJ1bmNoIG9mIG9yZGVycyB3aXRoIG5vIGRvbGxhcnMuIFdlIG1pZ2h0IHdhbnQgdG8gbG9vayBpbnRvIHRob3NlLiAKCjcuIEZpbmQgdGhlIG51bWJlciBvZiBzYWxlcyByZXBzIGluIGVhY2ggcmVnaW9uLiBZb3VyIGZpbmFsIHRhYmxlIHNob3VsZCBoYXZlIHR3byBjb2x1bW5zIC0gdGhlIHJlZ2lvbiBhbmQgdGhlIG51bWJlciBvZiBzYWxlc19yZXBzLiBPcmRlciBmcm9tIGZld2VzdCByZXBzIHRvIG1vc3QgcmVwcy4KYGBge3NxbH0KU0VMRUNUIHIubmFtZSwgQ09VTlQoKikgbnVtX3JlcHMKRlJPTSByZWdpb24gcgpKT0lOIHNhbGVzX3JlcHMgcwpPTiByLmlkID0gcy5yZWdpb25faWQKR1JPVVAgQlkgci5uYW1lCk9SREVSIEJZIG51bV9yZXBzOwpgYGAKCllvdSBjYW4gR1JPVVAgQlkgbXVsdGlwbGUgY29sdW1ucyBhdCBvbmNlLiBUaGlzIGlzIG9mdGVuIHVzZWZ1bCB0byBhZ2dyZWdhdGUgYWNyb3NzIGEgbnVtYmVyIG9mIGRpZmZlcmVudCBzZWdtZW50cy4gClRoZSBvcmRlciBvZiBjb2x1bW5zIGxpc3RlZCBpbiB0aGUgKk9SREVSIEJZKiBjbGF1c2UgZG9lcyBtYWtlIGEgZGlmZmVyZW5jZS4gWW91IGFyZSBvcmRlcmluZyB0aGUgY29sdW1ucyBmcm9tIGxlZnQgdG8gcmlnaHQuClRoZSBvcmRlciBvZiBjb2x1bW4gbmFtZXMgaW4geW91ciAqR1JPVVAgQlkqIGNsYXVzZSBkb2VzbuKAmXQgbWF0dGVy4oCUdGhlIHJlc3VsdHMgd2lsbCBiZSB0aGUgc2FtZSByZWdhcmRsZXNzLgoKIyMjIFF1ZXN0aW9ucwoxLiBGb3IgZWFjaCBhY2NvdW50LCBkZXRlcm1pbmUgdGhlIGF2ZXJhZ2UgYW1vdW50IG9mIGVhY2ggdHlwZSBvZiBwYXBlciB0aGV5IHB1cmNoYXNlZCBhY3Jvc3MgdGhlaXIgb3JkZXJzLiBZb3VyIHJlc3VsdCBzaG91bGQgaGF2ZSBmb3VyIGNvbHVtbnMgLSBvbmUgZm9yIHRoZSBhY2NvdW50IG5hbWUgYW5kIG9uZSBmb3IgdGhlIGF2ZXJhZ2Ugc3BlbnQgb24gZWFjaCBvZiB0aGUgcGFwZXIgdHlwZXMuCmBgYHtzcWx9ClNFTEVDVCBhLm5hbWUsIEFWRyhvLnN0YW5kYXJkX3F0eSkgYXZnX3N0YW5kLCBBVkcoby5nbG9zc19xdHkpIGF2Z19nbG9zcywgQVZHKG8ucG9zdGVyX3F0eSkgYXZnX3Bvc3QKRlJPTSBhY2NvdW50cyBhCkpPSU4gb3JkZXJzIG8KT04gYS5pZCA9IG8uYWNjb3VudF9pZApHUk9VUCBCWSBhLm5hbWU7CmBgYAoKMi4gRm9yIGVhY2ggYWNjb3VudCwgZGV0ZXJtaW5lIHRoZSBhdmVyYWdlIGFtb3VudCBzcGVudCBwZXIgb3JkZXIgb24gZWFjaCBwYXBlciB0eXBlLiBZb3VyIHJlc3VsdCBzaG91bGQgaGF2ZSBmb3VyIGNvbHVtbnMgLSBvbmUgZm9yIHRoZSBhY2NvdW50IG5hbWUgYW5kIG9uZSBmb3IgdGhlIGF2ZXJhZ2UgYW1vdW50IHNwZW50IG9uIGVhY2ggcGFwZXIgdHlwZS4KYGBge3NxbH0KU0VMRUNUIGEubmFtZSwgQVZHKG8uc3RhbmRhcmRfYW10X3VzZCkgYXZnX3N0YW5kLCBBVkcoby5nbG9zc19hbXRfdXNkKSBhdmdfZ2xvc3MsIEFWRyhvLnBvc3Rlcl9hbXRfdXNkKSBhdmdfcG9zdApGUk9NIGFjY291bnRzIGEKSk9JTiBvcmRlcnMgbwpPTiBhLmlkID0gby5hY2NvdW50X2lkCkdST1VQIEJZIGEubmFtZTsKYGBgCgozLiBEZXRlcm1pbmUgdGhlIG51bWJlciBvZiB0aW1lcyBhIHBhcnRpY3VsYXIgY2hhbm5lbCB3YXMgdXNlZCBpbiB0aGUgd2ViX2V2ZW50cyB0YWJsZSBmb3IgZWFjaCBzYWxlcyByZXAuIFlvdXIgZmluYWwgdGFibGUgc2hvdWxkIGhhdmUgdGhyZWUgY29sdW1ucyAtIHRoZSBuYW1lIG9mIHRoZSBzYWxlcyByZXAsIHRoZSBjaGFubmVsLCBhbmQgdGhlIG51bWJlciBvZiBvY2N1cnJlbmNlcy4gT3JkZXIgeW91ciB0YWJsZSB3aXRoIHRoZSBoaWdoZXN0IG51bWJlciBvZiBvY2N1cnJlbmNlcyBmaXJzdC4KYGBge3NxbH0KU0VMRUNUIHMubmFtZSwgdy5jaGFubmVsLCBDT1VOVCgqKSBudW1fZXZlbnRzCkZST00gYWNjb3VudHMgYQpKT0lOIHdlYl9ldmVudHMgdwpPTiBhLmlkID0gdy5hY2NvdW50X2lkCkpPSU4gc2FsZXNfcmVwcyBzCk9OIHMuaWQgPSBhLnNhbGVzX3JlcF9pZApHUk9VUCBCWSBzLm5hbWUsIHcuY2hhbm5lbApPUkRFUiBCWSBudW1fZXZlbnRzIERFU0M7CmBgYAoKNC4gRGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgdGltZXMgYSBwYXJ0aWN1bGFyIGNoYW5uZWwgd2FzIHVzZWQgaW4gdGhlIHdlYl9ldmVudHMgdGFibGUgZm9yIGVhY2ggcmVnaW9uLiBZb3VyIGZpbmFsIHRhYmxlIHNob3VsZCBoYXZlIHRocmVlIGNvbHVtbnMgLSB0aGUgcmVnaW9uIG5hbWUsIHRoZSBjaGFubmVsLCBhbmQgdGhlIG51bWJlciBvZiBvY2N1cnJlbmNlcy4gT3JkZXIgeW91ciB0YWJsZSB3aXRoIHRoZSBoaWdoZXN0IG51bWJlciBvZiBvY2N1cnJlbmNlcyBmaXJzdC4KYGBge3NxbH0KU0VMRUNUIHIubmFtZSwgdy5jaGFubmVsLCBDT1VOVCgqKSBudW1fZXZlbnRzCkZST00gYWNjb3VudHMgYQpKT0lOIHdlYl9ldmVudHMgdwpPTiBhLmlkID0gdy5hY2NvdW50X2lkCkpPSU4gc2FsZXNfcmVwcyBzCk9OIHMuaWQgPSBhLnNhbGVzX3JlcF9pZApKT0lOIHJlZ2lvbiByCk9OIHIuaWQgPSBzLnJlZ2lvbl9pZApHUk9VUCBCWSByLm5hbWUsIHcuY2hhbm5lbApPUkRFUiBCWSBudW1fZXZlbnRzIERFU0M7CmBgYAoK